﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestXNA3.Games;
using TestXNA3.Actors;
using TestXNA3.Components;
using TestXNA3.Resources;
using TestXNA3.Resources.Shaders;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using TestXNA3;
using TestXNA3.Render;

namespace TestEditor
{
    class EditorGameInfo : GameInfo
    {
        // grid
        public bool DrawGrid = true;

        // Movement
        public bool MouseLocked = false;

        public bool RightMousePressed = false;
        private float _rightMousePressedFor = 0.0f;

        public int centerX = 0;
        public int centerY = 0;

        public int skipRotationForFrames = 0;

        public float cameraSpeed = 768.0f;

        // Transformations
        public enum ETransformType
        {
            Translation,
            Rotation,
            Scaling
        };
        public ETransformType TransformType = ETransformType.Translation;

        public Actor SelectedActor = null;

        public enum ESelectedAxis
        {
            None,
            AxisX,
            AxisY,
            AxisZ
        };
        public ESelectedAxis SelectedAxis;
        public Vector3 SelectedAxisStartDrag;
        public BoundingBox SelectedActorRightBox;
        public BoundingBox SelectedActorUpBox;
        public BoundingBox SelectedActorForwardBox;

        public int formOffsetX = 0;
        public int formOffsetY = 0;

        public enum ESpaceType
        {
            World,
            Local
        };
        public ESpaceType SpaceType = ESpaceType.Local;

        // Snap
        public float LocationSnap = 4f;
        public float RotationSnap = 5f;
        public float ScaleSnap = 1f;

        public EditorGameInfo()
        {
        }

        public override void Initialize()
        {
            base.Initialize();


            MainCamera.Location = new Vector3(0, 16.0f, 32.0f);
            //MainCamera.RotateX(-90.0f);

            /*Actor testActor = new Actor();
            StaticMeshComponent sm = new StaticMeshComponent("plane_");
            ((StaticMeshDefaultShader)sm.StaticMesh.Shader).Diffuse = new TextureResource("wall");
            ((StaticMeshDefaultShader)sm.StaticMesh.Shader).Normal = new TextureResource("wall_NM_height");
            testActor.AttachComponent(sm);
            Scene.AttachActor(testActor);
            
            PointLight light = new PointLight();
            light.Location = new Vector3(0, 32.0f, 0);
            light.Diffuse = Color.AliceBlue;
            light.Radius = 384.0f;
            Scene.AttachActor(light);
           
            DirectionalLight sun = new DirectionalLight();
            sun.Yaw = -30.0f;
            sun.Pitch = -60.0f;
            sun.Diffuse = Color.AntiqueWhite;
            sun.Intensity = 1.0f;
            Scene.AttachActor(sun);*/
        }

        public Vector3 SnapLocation(Vector3 location)
        {
            int aX = (int)location.X / (int)LocationSnap;
            int aY = (int)location.Y / (int)LocationSnap;
            int aZ = (int)location.Z / (int)LocationSnap;

            return new Vector3(aX * LocationSnap, aY * LocationSnap, aZ * LocationSnap);
        }

        public override void Update(float dt)
        {
            KeyboardState currentKeyboardState = Keyboard.GetState();
            MouseState currentMouseState = Mouse.GetState();

            if (currentKeyboardState.IsKeyDown(Keys.Delete) && SelectedActor != null)
            {
                TestEditor.StaticTestEditor.actorProperties.SelectedObject = null;
                SelectedActor.Destroy();
                SelectedActor = null;
            }

            if (RightMousePressed)
            {
                _rightMousePressedFor += dt;

                if (_rightMousePressedFor > 0.25f && _rightMousePressedFor < 1.0f)
                {
                    MouseLocked = true;
                    centerX = currentMouseState.X;
                    centerY = currentMouseState.Y;
                    System.Windows.Forms.Cursor.Hide();
                    _rightMousePressedFor = 1f;
                }
            }
            else
            {
                if (_rightMousePressedFor < 0.25f && _rightMousePressedFor > 0.01f)
                {
                    TestEditor.StaticTestEditor.notifyRightMousePressed();
                }

                MouseLocked = false;
                _rightMousePressedFor = 0f;
                System.Windows.Forms.Cursor.Show();
            }

            if (TransformType == ETransformType.Rotation && currentMouseState.LeftButton == ButtonState.Pressed)
            {
                float x = currentMouseState.X;
                float y = currentMouseState.Y;

                x -= centerX;
                y -= centerY;

                float angle = -(((float)(currentMouseState.X - centerX)));

                int snappedAngle = (int)angle / (int)RotationSnap;
                angle = snappedAngle * RotationSnap;

                if (skipRotationForFrames <= 0)
                {
                    switch (SelectedAxis)
                    {
                        case EditorGameInfo.ESelectedAxis.AxisX:
                            SelectedActor.Yaw += angle;
                            break;
                        case ESelectedAxis.AxisY:
                            SelectedActor.Pitch += angle;
                            break;
                        case ESelectedAxis.AxisZ:
                            SelectedActor.Roll += angle;
                            break;
                    }
                }
                else
                {
                    skipRotationForFrames--;
                }

                if (angle != 0)
                {
                    centerX = currentMouseState.X;
                    centerY = currentMouseState.Y;
                    Mouse.SetPosition(centerX, centerY);
                }
            }

            if (MouseLocked)
            {
                if (currentKeyboardState.IsKeyDown(Keys.W))
                {
                    MainCamera.Location = MainCamera.Location + MainCamera.Forward * cameraSpeed * dt;
                }
                if (currentKeyboardState.IsKeyDown(Keys.S))
                {
                    MainCamera.Location = MainCamera.Location - MainCamera.Forward * cameraSpeed * dt;
                }

                if (currentKeyboardState.IsKeyDown(Keys.A))
                {
                    MainCamera.Location = MainCamera.Location - MainCamera.Right * cameraSpeed * dt;
                }
                if (currentKeyboardState.IsKeyDown(Keys.D))
                {
                    MainCamera.Location = MainCamera.Location + MainCamera.Right * cameraSpeed * dt;
                }

                float x = currentMouseState.X;
                float y = currentMouseState.Y;

                x -= centerX;
                y -= centerY;

                MainCamera.Yaw += -(((float)(currentMouseState.X - centerX)) * 0.1f);
                MainCamera.Pitch += -(((float)(currentMouseState.Y - centerY)) * 0.1f);


                Mouse.SetPosition(centerX, centerY);
            }
            /*else
            if (currentMouseState.LeftButton == ButtonState.Pressed)
            {
                if (SelectedActor != null && SelectedAxis != ESelectedAxis.None)
                {
                    Plane p = new Plane();
                    Vector3 axis = Vector3.Zero;
                    if (SpaceType == ESpaceType.Local)
                    {
                        switch (SelectedAxis)
                        {
                            case ESelectedAxis.AxisX:
                                axis = SelectedActor.Right;
                                p = new Plane(SelectedActor.Location + SelectedActor.Up
                                    , SelectedActor.Location + SelectedActor.Right, SelectedActor.Location);
                                break;

                            case ESelectedAxis.AxisY:
                                axis = SelectedActor.Up;
                                p = new Plane(SelectedActor.Location + SelectedActor.Up
                                    , SelectedActor.Location + SelectedActor.Forward, SelectedActor.Location);
                                break;

                            case ESelectedAxis.AxisZ:
                                axis = -SelectedActor.Forward;
                                p = new Plane(SelectedActor.Location + SelectedActor.Forward
                                    , SelectedActor.Location + SelectedActor.Up, SelectedActor.Location);
                                break;
                        };
                    }

                    Vector3 startTrace = MainCamera.Unproject(new Vector3(System.Windows.Forms.Cursor.Position.X - formOffsetX, System.Windows.Forms.Cursor.Position.Y - formOffsetY - (int)SelectedAxisStartDrag.X, 0.0f));
                    Vector3 endTrace = MainCamera.Unproject(new Vector3(System.Windows.Forms.Cursor.Position.X - formOffsetX, System.Windows.Forms.Cursor.Position.Y - formOffsetY - (int)SelectedAxisStartDrag.Y, 1.0f));

                    Vector3 traceDir = endTrace - startTrace;
                    traceDir.Normalize();
                    Ray ray = new Ray(startTrace, traceDir);

                    float? t = ray.Intersects(p);
                    if (t.HasValue)
                    {
                        Vector3 intersectionPoint = startTrace + traceDir * t.Value;

                        Vector3 toPoint = intersectionPoint - SelectedActor.Location;
                        toPoint.Normalize();
                        float dist = (intersectionPoint - SelectedActor.Location).Length();

                        //BoundingBox box = new BoundingBox(intersectionPoint + new Vector3(10f), intersectionPoint - new Vector3(10f));
                        //SceneRenderer.DrawDebugBox(box, Color.Chartreuse, true);
                        if (dist > 1f)
                        {
                            Vector3 dir = axis * toPoint;

                            Vector3 snappedLocation = SelectedActor.Location + dir * dist;
                            SelectedActor.Location = SnapLocation(snappedLocation);
                        }
                    }

                    //SelectedActor.Location = (
                }
            }*/

            base.Update(dt);
        }

        public float calcAxisExtension()
        {
            Vector3 actorPosToCamera = SelectedActor.Location - MainCamera.Location;
            float distanceToActor = actorPosToCamera.Length();
            distanceToActor *= 0.1f;
            distanceToActor = (distanceToActor < 1.0f) ? 1.0f : distanceToActor;

            return distanceToActor;
        }

        public override void Draw(float dt)
        {
            if (SelectedActor != null)
            {
                float distanceToActor = calcAxisExtension();

                Vector3 generalMin = SelectedActor.Location;
                Vector3 rightMax = generalMin + Vector3.Right * distanceToActor +
                    Vector3.Up * distanceToActor * 0.1f + Vector3.Forward * distanceToActor * 0.1f;
                Vector3 upMax = generalMin + Vector3.Up * distanceToActor +
                    Vector3.Right * distanceToActor * 0.1f + Vector3.Forward * distanceToActor * 0.1f;
                Vector3 forwardMax = generalMin + Vector3.Forward * distanceToActor +
                    Vector3.Up * distanceToActor * 0.1f + Vector3.Right * distanceToActor * 0.1f;

                BoundingBox rightBox = new BoundingBox(generalMin, rightMax);
                BoundingBox upBox = new BoundingBox(generalMin, upMax);
                BoundingBox forwardBox = new BoundingBox(generalMin, forwardMax);

                SceneRenderer.DrawDebugBox(rightBox, Microsoft.Xna.Framework.Graphics.Color.Red, false, true);
                SceneRenderer.DrawDebugBox(upBox, Microsoft.Xna.Framework.Graphics.Color.Green, false, true);
                SceneRenderer.DrawDebugBox(forwardBox, Microsoft.Xna.Framework.Graphics.Color.Blue, false, true);

                SelectedActorRightBox = rightBox;
                SelectedActorUpBox = upBox;
                SelectedActorForwardBox = forwardBox;
            }

            if (DrawGrid)
            {
                float cellSize = 32f;
                float cellMultiplier = 1f;

                int cameraDistance = (int)(MainCamera.Location - Vector3.Zero).Length();

                cellMultiplier = (int)Math.Max(cameraDistance / 128, 1);

                for (int x = -32; x <= 32; x++)
                {
                    SceneRenderer.DrawDebugLine(new Vector3((float)x * LocationSnap * cellMultiplier, 0, -cellSize * LocationSnap * cellMultiplier)
                        , new Vector3((float)x * LocationSnap * cellMultiplier, 0, cellSize * LocationSnap * cellMultiplier), (x == 0) ? Color.White : Color.Gray, false, false);
                }

                for (int z = -32; z <= 32; z++)
                {
                    SceneRenderer.DrawDebugLine(new Vector3(-cellSize * LocationSnap * cellMultiplier, 0, (float)z * LocationSnap * cellMultiplier)
                        , new Vector3(cellSize * LocationSnap * cellMultiplier, 0, (float)z * LocationSnap * cellMultiplier), (z == 0) ? Color.White : Color.Gray, false, false);
                }
            }
            base.Draw(dt);
        }
    }
}
